
#ifndef __SSH_CLIENT_H
#define __SSH_CLIENT_H

#include <libssh2.h>
#include "../db/PendingDB.h"
#include "../net/TCPClient.h"
#include "SecurityChain.h"

// Number of microsecond for the maximum sleep between EAGAIN
// returning non-blocking calls
#define SSH_TIMEOUT_EAGAIN 0x1000

/** SSH client class based on TCPClient. */
class SSHClient : public TCPClient
{
   public:
      SSHClient(const char *ihostname, int iportno, apr_pool_t *imp,
         const char *ikeyprint, int ikeyprintlen, const char *irealservername, int irealserverport,
         PendingDB *ipendingdb);
      virtual ~SSHClient();
      /** Gets the active session pointer, needed to use the SSH link
       * to provide various channelled services.
       * 
       * @return A pointer to the active session. */
      const LIBSSH2_SESSION *getSSHSession() const { return tsession; }
      int useSecurityChain(SecurityChain *secchain, const char *username);
      /** Determines whether the SSH link can be used.
       * 
       * @return Non-zero if the link is usable, zero if the link is
       * down. */
      int isUp() { return ((isvalid) && (libssh2_userauth_authenticated(tsession))); }
      /** Gets the real server name (so if this is a tunnel to a
       * remote SSH server, this will give what the class was told is
       * the 'real' hostname.
       * 
       * @return String containing the real host name of the server to
       * which this class and client is linked. */
      const char *getRealServerName() { return realservername; }
      /** Gets the real server port (not the local one to which this
       * is forwarded if this is a forwarded SSH link).
       * 
       * @return Real port number for the remote service (to which
       * data is going on the final hop). */
      int getRealServerPort() { return realserverport; }
      /** Gets the local port number (this is by preference the local
       * port to which a remote connection is forwarded, or if the
       * connection is remote but direct, it will get the direct port
       * number.
       * 
       * @return The real port location to which data is going
       * first. */
      int getLocalConnServerPort() { return localconnserverport; }
      /** Gets a mutex which all channels can use for mutual
       * exclusion.
       * 
       * @return The lock as an APRMutex convenience wrapper. */
      APRMutex *getMutex() { return mutex; }
   private:
      void initClient(const char *ikeyprint, int ikeyprintlen, const char *irealservername);
      int doSSHSessionHandshake();
      /** The mutex wrapped in a convenience class. */
      APRMutex *mutex;
      /** The key fingerprint in binary, or NULL if the key
       * fingerprint is not to be checked (generates a warning!). */
      char *keyprint;
      /** The binary key fingerprint length in bytes (if it
       * exists). */
      int keyprintlen;
      /** The real server name (final hop). */
      char *realservername;
      /** The real server port (final hop). */
      int realserverport;
      /** The local server port (first hop). */
      int localconnserverport;
      /** The pending administrative operations database (if any host
       * key fingerprints fail, if this is not NULL they will be added
       * to the pending changes database). */
      PendingDB *pendingdb;
      /** The current SSH session pointer. */
      LIBSSH2_SESSION *tsession;
};

#endif
